package cn.patient.pp.module.patients.service;

import cn.patient.pp.framework.common.pojo.PageParam;
import cn.patient.pp.framework.common.pojo.PageResult;
import cn.patient.pp.framework.security.core.LoginUser;
import cn.patient.pp.framework.security.core.util.SecurityFrameworkUtils;
import cn.patient.pp.module.patients.controller.admin.vo.*;
import cn.patient.pp.module.patients.controller.admin.vo.binscreen.BigScreenCheckPatient;
import cn.patient.pp.module.patients.controller.admin.vo.binscreen.SpectedMentalDisorderVoDetail;
import cn.patient.pp.module.patients.controller.admin.vo.binscreen.SuspectedMentalDisorderVo;
import cn.patient.pp.module.patients.dal.dataobject.patients.FollowUpDO;
import cn.patient.pp.module.patients.dal.dataobject.patients.PatientsDODO;
import cn.patient.pp.module.patients.dal.mysql.patients.BigScreenMapper;
import cn.patient.pp.module.patients.dal.mysql.patients.FollowUpMapper;
import cn.patient.pp.module.patients.dal.mysql.patients.PatientsDOMapper;
import cn.patient.pp.module.report.dal.dataobject.report.PatientInfoDO;
import cn.patient.pp.module.report.dal.mysql.report.ReportMapper;
import cn.patient.pp.module.system.api.sms.SmsSendApi;
import cn.patient.pp.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO;
import cn.patient.pp.module.system.dal.dataobject.dept.DeptDO;
import cn.patient.pp.module.system.dal.dataobject.dict.DictDataDO;
import cn.patient.pp.module.system.dal.dataobject.user.AdminUserDO;
import cn.patient.pp.module.system.service.dept.DeptService;
import cn.patient.pp.module.system.service.dict.DictDataService;
import cn.patient.pp.module.system.service.user.AdminUserService;
import com.alibaba.excel.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.patient.pp.module.patients.framework.web.config.Config.HAS_DIAGNOSED_PATIENT_CODE;
import static cn.patient.pp.module.system.dal.redis.RedisKeyConstants.*;

@Service
@Validated
public class BigScreenServiceImpl implements BigScreenService {

    @Resource
    private BigScreenMapper bigScreenMapper;

    @Resource
    private DictDataService dictDataService;

    @Resource
    private PatientsDOMapper dOMapper;

    @Resource
    private FollowUpMapper followUpMapper;

    @Resource
    private DeptService deptService;

    @Resource
    private AdminUserService adminUserService;

    @Resource
    private SmsSendApi smsSendApi;

    @Resource
    private ReportMapper reportMapper;

    @Override
    @Cacheable(
            cacheNames = RISK_PROPORTION,
            key        = "'risk_proportion_'+#reqVO.getUserId()+'_'+#reqVO.getTimeType()+'_'+#reqVO.getDeptId()"
    )
    public RiskProportionRespVO getRiskProportion(BigScreenReqVO reqVO) {

        reqVO.setDeptIds(getDeptIds(reqVO.getDeptId()));

        RiskProportionRespVO respVO = new RiskProportionRespVO();

        // 查询总数
        Integer total = bigScreenMapper.selectTotalDistinctPatients(reqVO);
        respVO.setTotal(total == null ? 0 : total);

        // 查询医院排名前三
        List<Map<String, Object>> hospitalList = bigScreenMapper.selectTopThreeHospitals(reqVO);
        if (hospitalList != null && !hospitalList.isEmpty()) {
            if (hospitalList.size() >= 1) {
                Map<String, Object> first = hospitalList.get(0);
                respVO.setFirstHospitalName(String.valueOf(first.get("hospitalName")));
                respVO.setFirstCount(((Number) first.get("cnt")).intValue());
            }
            if (hospitalList.size() >= 2) {
                Map<String, Object> second = hospitalList.get(1);
                respVO.setSecondHospitalName(String.valueOf(second.get("hospitalName")));
                respVO.setSecondCount(((Number) second.get("cnt")).intValue());
            }
            if (hospitalList.size() >= 3) {
                Map<String, Object> third = hospitalList.get(2);
                respVO.setThirdHospitalName(String.valueOf(third.get("hospitalName")));
                respVO.setThirdCount(((Number) third.get("cnt")).intValue());
            }
        }

        return respVO;
    }

    @Override
    public List<Long> getDeptIds(Long inputDeptId) {
        // 获取当前登录用户
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        AdminUserDO user = adminUserService.getUser(loginUser.getId());
        // 获取用户的部门 ID 列表
        List<Long> deptIds = deptService.getDeptIdsAndOwnId(user.getDeptId());
        if (inputDeptId == null || inputDeptId == 0) {
            return deptIds;
        } else {
            return deptService.getDeptIdsAndOwnId(inputDeptId);
        }
    }

    @Override
    public List<DeptDO> getDeptIdsByParentId(Long inputDeptId) {
        if (inputDeptId == null || inputDeptId == 0) {
            // 获取当前登录用户
            LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
            AdminUserDO user = adminUserService.getUser(loginUser.getId());
            // 获取用户的部门 ID 列表
            List<DeptDO> deptIds = deptService.getChildDeptIdListByParentId(user.getDeptId());
            return deptIds;
        } else {
            List<DeptDO> inputDeptIds = deptService.getChildDeptIdListByParentId(inputDeptId);
            return inputDeptIds;
        }
    }

    @Override
    @Cacheable(
            cacheNames = RISK_TYPE_PROPORTION,
            key        = "'risk_type_proportion_'+#reqVO.getUserId()+'_'+#reqVO.getTimeType()+'_'+#reqVO.getDeptId()"
    )
    public List<RiskTypeProportionRespVO> getRiskTypeProportion(BigScreenReqVO reqVO) {
        // 1. 拆解部门
        reqVO.setDeptIds(getDeptIds(reqVO.getDeptId()));

        // 2. 取出所有 risk_assessment 的字典项
        List<DictDataDO> riskTypeList =
                dictDataService.getDictDataListByDictType("risk_assessment");

        // 3. 用新写的原生 SQL，一次性拿到“每个患者最新一次随访”的评估分布
        List<Map<String, Object>> rawCounts =
                dOMapper.selectLatestRiskCounts(reqVO.getDeptIds());

        // 4. 转成 Map<code, count>
        Map<String, Long> countMap = rawCounts.stream().collect(Collectors.toMap(
                m -> (String) m.get("risk_assessment"),
                m -> ((Number) m.get("cnt")).longValue()
        ));

        // 5. 合并字典，补 0，返回 VO 列表
        return riskTypeList.stream()
                .map(dict -> {
                    RiskTypeProportionRespVO vo = new RiskTypeProportionRespVO();
                    vo.setRiskTypeCode(dict.getValue());
                    vo.setRiskTypeName(dict.getLabel());
                    vo.setRiskTypeCount(countMap.getOrDefault(dict.getValue(), 0L));
                    return vo;
                })
                .collect(Collectors.toList());
    }

    @Override
    @Cacheable(
            cacheNames = INCREASE_PROPORTION,
            key        = "'INCREASE_PROPORTION_'+#reqVO.getUserId()+'_'+#reqVO.getTimeType()+'_'+#reqVO.getDeptId()"
    )
    public List<IncreaseProportionRespVO> getIncreaseProportion(BigScreenReqVO reqVO) {
        reqVO.setDeptIds(getDeptIds(reqVO.getDeptId()));

        // 1. 准备时间边界
        String earliestStr = reqVO.getStartTime();
        String tomorrowStr = reqVO.getEndTime();

        String timeType = reqVO.getTimeType();

        // 2. 查询每个 id_number 的录入日期，并只保留在边界内的
        // 2. 查询每个 id_number 的首次录入日期，并只保留在边界内的
        List<Map<String, Object>> firstRecords = followUpMapper.selectMaps(
                new QueryWrapper<FollowUpDO>()
                        .select("id_number", "MIN(entry_date) AS first_time")
                        .in("dept_id", reqVO.getDeptIds())
                        .ge("entry_date", earliestStr)
                        .lt("entry_date", tomorrowStr)
                        .groupBy("id_number")
        );

        DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyyMMdd");
        Map<LocalDate, Long> dayCount = new HashMap<>();
        Map<YearMonth, Long> monthCount = new HashMap<>();

        if ("01".equals(timeType)) {
            dayCount = firstRecords.stream()
                    .map(m -> LocalDate.parse((String) m.get("first_time"), DATE_FMT))
                    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
        } else {
            monthCount = firstRecords.stream()
                    .map(m -> {
                        LocalDate d = LocalDate.parse((String) m.get("first_time"), DATE_FMT);
                        return YearMonth.from(d);
                    })
                    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
        }

        // 4. 构造结果：按 timeType 决定取最近 N 天或最近 N 个月
        DateTimeFormatter dtfDate  = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        DateTimeFormatter dtfMonth = DateTimeFormatter.ofPattern("yyyy-MM");
        LocalDate     today       = LocalDate.now();
        YearMonth     thisMonth   = YearMonth.now();

        List<IncreaseProportionRespVO> result = new ArrayList<>();

        switch (timeType) {
            case "01":  // 最近 7 天
                for (int i = 6; i >= 0; i--) {
                    LocalDate day = today.minusDays(i);
                    IncreaseProportionRespVO vo = new IncreaseProportionRespVO();
                    vo.setIncreaseTime(day.format(dtfDate));
                    vo.setIncreaseCount(dayCount.getOrDefault(day, 0L));
                    result.add(vo);
                }
                break;

            case "02":  // 最近 6 个月
                for (int i = 5; i >= 0; i--) {
                    YearMonth ym = thisMonth.minusMonths(i);
                    IncreaseProportionRespVO vo = new IncreaseProportionRespVO();
                    vo.setIncreaseTime(ym.format(dtfMonth));
                    vo.setIncreaseCount(monthCount.getOrDefault(ym, 0L));
                    result.add(vo);
                }
                break;

            case "03":  // 最近 12 个月
                for (int i = 11; i >= 0; i--) {
                    YearMonth ym = thisMonth.minusMonths(i);
                    IncreaseProportionRespVO vo = new IncreaseProportionRespVO();
                    vo.setIncreaseTime(ym.format(dtfMonth));
                    vo.setIncreaseCount(monthCount.getOrDefault(ym, 0L));
                    result.add(vo);
                }
                break;

            default:
                // 如果 timeType 不在预期值里，可抛异常或返回空列表
                break;
        }

        return result;
    }

    @Override
    @Cacheable(
            cacheNames = HIGH_RISK_PATIENTS,
            key        = "'high_risk_patients'+#reqVO.getUserId()+'_'+#reqVO.getTimeType()+'_'+#reqVO.getDeptId()"
    )
    public List<HighRiskPatientsRespVO> getHighRiskPatients(BigScreenReqVO reqVO) {
        DateTimeFormatter DATE_FMT_IN = DateTimeFormatter.ofPattern("yyyyMMdd");
        DateTimeFormatter dateFmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        DateTimeFormatter monthFmt = DateTimeFormatter.ofPattern("yyyy-MM");

        reqVO.setDeptIds(getDeptIds(reqVO.getDeptId()));

        String startStr = reqVO.getStartTime();
        String endStr = reqVO.getEndTime();

        Map<String, Object> params = new HashMap<>();
        params.put("deptIds", reqVO.getDeptIds());
        params.put("start", startStr);
        params.put("end", endStr);

        // 查询得到：每条包含 followUpDate
        List<FollowUpDO> firstHighRisk = followUpMapper.getHighRiskPatients(params);

        // Step 1: 按原始日期（yyyyMMdd）做基础分组
        Map<String, Long> firstHighRiskStatic = firstHighRisk.stream()
                .collect(Collectors.groupingBy(
                        FollowUpDO::getFollowUpDate,
                        Collectors.counting()
                ));

        // Step 2: 按粒度归集
        Map<LocalDate, Long> dayCount = new HashMap<>();
        Map<YearMonth, Long> monthCount = new HashMap<>();

        if ("01".equals(reqVO.getTimeType())) {
            // 按天
            dayCount = firstHighRiskStatic.entrySet().stream()
                    .collect(Collectors.toMap(
                            e -> LocalDate.parse(e.getKey(), DATE_FMT_IN),
                            Map.Entry::getValue
                    ));
        } else {
            // 按月
            monthCount = firstHighRiskStatic.entrySet().stream()
                    .collect(Collectors.groupingBy(
                            e -> YearMonth.from(LocalDate.parse(e.getKey(), DATE_FMT_IN)),
                            Collectors.summingLong(Map.Entry::getValue)
                    ));
        }

        // Step 3: 构造返回
        LocalDate today = LocalDate.now();
        YearMonth thisMonth = YearMonth.now();
        List<HighRiskPatientsRespVO> result = new ArrayList<>();

        switch (reqVO.getTimeType()) {
            case "01": // 最近 7 天
                for (int i = 6; i >= 0; i--) {
                    LocalDate d = today.minusDays(i);
                    HighRiskPatientsRespVO vo = new HighRiskPatientsRespVO();
                    vo.setMonth(d.format(dateFmt));
                    vo.setCount(dayCount.getOrDefault(d, 0L));
                    result.add(vo);
                }
                break;
            case "02": // 最近 6 个月
                for (int i = 5; i >= 0; i--) {
                    YearMonth ym = thisMonth.minusMonths(i);
                    HighRiskPatientsRespVO vo = new HighRiskPatientsRespVO();
                    vo.setMonth(ym.format(monthFmt));
                    vo.setCount(monthCount.getOrDefault(ym, 0L));
                    result.add(vo);
                }
                break;
            case "03": // 最近 12 个月
                for (int i = 11; i >= 0; i--) {
                    YearMonth ym = thisMonth.minusMonths(i);
                    HighRiskPatientsRespVO vo = new HighRiskPatientsRespVO();
                    vo.setMonth(ym.format(monthFmt));
                    vo.setCount(monthCount.getOrDefault(ym, 0L));
                    result.add(vo);
                }
                break;
            default:
                throw new IllegalArgumentException("Unsupported timeType: " + reqVO.getTimeType());
        }

        return result;
    }

    @Override
    @Cacheable(
            cacheNames = FOLLOW_IP_TRENDS,
            key        = "'followUp_trends'+#reqVO.getUserId()+'_'+#reqVO.getTimeType()+'_'+#reqVO.getDeptId()"
    )
    public List<FollowUpTrendsRespVO> getFollowUpTrends(BigScreenReqVO reqVO) {
        // 0. 准备 deptIds 和 时间边界
        reqVO.setDeptIds(getDeptIds(reqVO.getDeptId()));
        String startStr  = reqVO.getStartTime();
        String endStr    = reqVO.getEndTime();
        String timeType  = reqVO.getTimeType();

        // 格式化器
        DateTimeFormatter dayKeyFmt   = DateTimeFormatter.ofPattern("yyyyMMdd");
        DateTimeFormatter dayLabelFmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        DateTimeFormatter monKeyFmt   = DateTimeFormatter.ofPattern("yyyyMM");
        DateTimeFormatter monLabelFmt = DateTimeFormatter.ofPattern("yyyy-MM");

        // Map 用于存聚合结果
        Map<String, Long> finishMap   = new HashMap<>();
        Map<String, Long> unfinishMap = new HashMap<>();

        // 1. 根据粒度选择不同的 SQL
        if ("01".equals(timeType)) {
            // 按日
            // 面访
            List<Map<String,Object>> finishMaps = followUpMapper.selectMaps(
                    new QueryWrapper<FollowUpDO>()
                            .select("LEFT(follow_up_date,8) AS ym", "COUNT(*) AS cnt")
                            .in("dept_id", reqVO.getDeptIds())
                            .ne("follow_up_mode", "3")
                            .ge("follow_up_date", startStr)
                            .lt("follow_up_date", endStr)
                            .groupBy("LEFT(follow_up_date,8)")
            );
            finishMap = finishMaps.stream()
                    .collect(Collectors.toMap(
                            m -> (String)m.get("ym"),
                            m -> ((Number)m.get("cnt")).longValue()
                    ));

            // 电话访问
            List<Map<String,Object>> unfinishMaps = followUpMapper.selectMaps(
                    new QueryWrapper<FollowUpDO>()
                            .select("LEFT(follow_up_date,8) AS ym", "COUNT(*) AS cnt")
                            .in("dept_id", reqVO.getDeptIds())
                            .eq("follow_up_mode", "3")
                            .ge("follow_up_date", startStr)
                            .lt("follow_up_date", endStr)
                            .groupBy("LEFT(follow_up_date,8)")
            );
            unfinishMap = unfinishMaps.stream()
                    .collect(Collectors.toMap(
                            m -> (String)m.get("ym"),
                            m -> ((Number)m.get("cnt")).longValue()
                    ));

        } else {
            // 按月（02->6 个月，03->12 个月）
            // 面访
            List<Map<String,Object>> finishMaps = followUpMapper.selectMaps(
                    new QueryWrapper<FollowUpDO>()
                            .select("LEFT(follow_up_date,6) AS ym", "COUNT(*) AS cnt")
                            .in("dept_id", reqVO.getDeptIds())
                            .ne("follow_up_mode", "3")
                            .ge("follow_up_date", startStr)
                            .lt("follow_up_date", endStr)
                            .groupBy("LEFT(follow_up_date,6)")
            );
            finishMap = finishMaps.stream()
                    .collect(Collectors.toMap(
                            m -> (String)m.get("ym"),
                            m -> ((Number)m.get("cnt")).longValue()
                    ));

            //  电话访问
            List<Map<String,Object>> unfinishMaps = followUpMapper.selectMaps(
                    new QueryWrapper<FollowUpDO>()
                            .select("LEFT(follow_up_date,6) AS ym", "COUNT(*) AS cnt")
                            .eq("follow_up_mode", "3")
                            .in("dept_id", reqVO.getDeptIds())
                            .ge("follow_up_date", startStr)
                            .lt("follow_up_date", endStr)
                            .groupBy("LEFT(follow_up_date,6)")
            );
            unfinishMap = unfinishMaps.stream()
                    .collect(Collectors.toMap(
                            m -> (String)m.get("ym"),
                            m -> ((Number)m.get("cnt")).longValue()
                    ));
        }

        // 2. 构造结果列表
        List<FollowUpTrendsRespVO> result = new ArrayList<>();
        LocalDate today     = LocalDate.now();
        YearMonth thisMonth = YearMonth.now();

        switch (timeType) {
            case "01":
                // 最近 7 天
                for (int i = 6; i >= 0; i--) {
                    LocalDate d = today.minusDays(i);
                    String key   = d.format(dayKeyFmt);    // "yyyyMMdd"
                    FollowUpTrendsRespVO vo = new FollowUpTrendsRespVO();
                    vo.setMonth(d.format(dayLabelFmt));    // "yyyy-MM-dd"
                    vo.setFaceCount(finishMap.getOrDefault(key, 0L));
                    vo.setPhoneCount(unfinishMap.getOrDefault(key, 0L));
                    result.add(vo);
                }
                break;
            case "02":
                // 最近 6 个月
                for (int i = 5; i >= 0; i--) {
                    YearMonth ym = thisMonth.minusMonths(i);
                    String key   = ym.format(monKeyFmt);   // "yyyyMM"
                    FollowUpTrendsRespVO vo = new FollowUpTrendsRespVO();
                    vo.setMonth(ym.format(monLabelFmt));   // "yyyy-MM"
                    vo.setFaceCount(finishMap.getOrDefault(key, 0L));
                    vo.setPhoneCount(unfinishMap.getOrDefault(key, 0L));
                    result.add(vo);
                }
                break;
            case "03":
                // 最近 12 个月
                for (int i = 11; i >= 0; i--) {
                    YearMonth ym = thisMonth.minusMonths(i);
                    String key   = ym.format(monKeyFmt);
                    FollowUpTrendsRespVO vo = new FollowUpTrendsRespVO();
                    vo.setMonth(ym.format(monLabelFmt));
                    vo.setFaceCount(finishMap.getOrDefault(key, 0L));
                    vo.setPhoneCount(unfinishMap.getOrDefault(key, 0L));
                    result.add(vo);
                }
                break;
            default:
                throw new IllegalArgumentException("Unsupported timeType: " + timeType);
        }

        return result;
    }

    @Override
    public PageResult<AlarmRespVO> getAlarm(AlarmReqVO reqVO) {

        if (reqVO.getDeptIds() == null || reqVO.getDeptIds().isEmpty()) {
            reqVO.setDeptIds(getDeptIds(reqVO.getDeptId()));
        }

        String alarmType = reqVO.getAlarmType();

        int pageNo = reqVO.getPageNo();
        int pageSize = reqVO.getPageSize();
        int offset = (pageNo - 1) * pageSize;

        if ("1".equals(alarmType)) {
            Page<PatientsDODO> page = new Page<>(pageNo, pageSize);

            QueryWrapper<PatientsDODO> wrapper = new QueryWrapper<>();
            // 手动写 select，把 id_number 作为分组字段，
            // last_followup_date 取最小值（最久未随访），
            // 其它字段用 ANY_VALUE() 拿任意一条
            wrapper.select(
                            "id_number",
                            "MIN(last_followup_date) AS last_followup_date",
                            "ANY_VALUE(patient_name)       AS patient_name",
                            "ANY_VALUE(management_org_name) AS management_org_name",
                            "ANY_VALUE(gender)             AS gender",
                            "ANY_VALUE(age)                AS age",
                            "ANY_VALUE(patient_code)       AS patient_code",
                            "ANY_VALUE(dept_id)           AS dept_id",
                            "ANY_VALUE(contact_name)       AS contact_name",
                            "ANY_VALUE(contact_phone)       AS contact_phone",
                            "ANY_VALUE(current_address)       AS current_address"
                    )
                    .in("dept_id", reqVO.getDeptIds())
                    .notInSql("id_number", "SELECT DISTINCT id_number FROM follow_up_list")
                    .eq("informed_consent", "同意参加社区服务管理")
                    .groupBy("id_number")
                    .orderByAsc("MIN(last_followup_date)");

            // 可选的额外过滤
            if (StringUtils.isNotBlank(reqVO.getPatientName())) {
                wrapper.like("patient_name", reqVO.getPatientName());
            }
            if (StringUtils.isNotBlank(reqVO.getIdNumber())) {
                wrapper.eq("id_number", reqVO.getIdNumber());
            }
            if (StringUtils.isNotBlank(reqVO.getDeptName())) {
                wrapper.like("management_org_name", reqVO.getDeptName());
            }

            IPage<PatientsDODO> patientPage = dOMapper.selectPage(page, wrapper);

            List<AlarmRespVO> alarms = patientPage.getRecords().stream()
                    .map(base -> {
                        AlarmRespVO vo = new AlarmRespVO();
                        vo.setPatientName(base.getPatientName());
                        vo.setIdNumber(base.getIdNumber());
                        vo.setDeptName(base.getManagementOrgName());
                        vo.setLastFollowUpDate(base.getLastFollowupDate());
                        vo.setGender(base.getGender());
                        vo.setAge(base.getAge());
                        vo.setNextFollowUpDate(base.getNextFollowupDate());
                        vo.setPatientCode(base.getPatientCode());
                        vo.setDeptId(base.getDeptId());
                        vo.setCurrentAddress(base.getCurrentAddress());
                        vo.setContactName(base.getContactName());
                        vo.setContactPhone(base.getContactPhone());
                        return vo;
                    })
                    .collect(Collectors.toList());

            PageResult<AlarmRespVO> result = new PageResult<>();
            result.setList(alarms);
            result.setTotal(patientPage.getTotal());
            return result;
        }

        // 1. 计算 "7天后"用于判断高风险超期
        String cutoff7 = LocalDate.now()
                .plusDays(7)
                .format(DateTimeFormatter.ofPattern("yyyyMMdd"));
                
        // 2. 计算"15天前"用于判断超期
        String cutoff15 = LocalDate.now()
                .plusDays(15)
                .format(DateTimeFormatter.ofPattern("yyyyMMdd"));

        String nowDate = LocalDate.now()
                .format(DateTimeFormatter.ofPattern("yyyyMMdd"));

                
        // 3. 使用SQL直接获取分页过滤后的数据
        List<FollowUpDO> alarmList = followUpMapper.selectAlarmByPage(nowDate, cutoff7, cutoff15,
                reqVO.getPageNo(), reqVO.getPageSize(), reqVO.getDeptIds(), reqVO.getDeptName(),reqVO.getPatientName(), reqVO.getIdNumber());
        
        // 如果没有数据，返回空分页结果
        if (alarmList == null || alarmList.isEmpty()) {
            PageResult<AlarmRespVO> pageResult = new PageResult<>();
            pageResult.setTotal(0L);
            pageResult.setList(new ArrayList<>());
            return pageResult;
        }
        
        // 4. 封装报警数据
        List<AlarmRespVO> alarms = new ArrayList<>(alarmList.size());
        alarmList.forEach(latestPatient -> {
            AlarmRespVO alarm = new AlarmRespVO();
            alarm.setPatientName(latestPatient.getPatientName());
            alarm.setIdNumber(latestPatient.getIdNumber());
            alarm.setDeptName(latestPatient.getManagementOrgName());
            alarm.setLastFollowUpDate(latestPatient.getFollowUpDate());
            alarm.setGender(latestPatient.getGender());
            alarm.setAge(latestPatient.getAge());
            alarm.setPatientCode(latestPatient.getPatientCode());
            alarm.setDeptId(latestPatient.getDeptId());
            alarm.setNextFollowUpDate(latestPatient.getNextFollowUpDate());
            alarms.add(alarm);
        });
        
        // 5. 获取总记录数
        Long total = followUpMapper.countAlarm(nowDate, cutoff7, cutoff15, reqVO.getDeptIds(), reqVO.getDeptName(),reqVO.getPatientName(), reqVO.getIdNumber());
        
        // 6. 装入 PageResult
        PageResult<AlarmRespVO> pageResult = new PageResult<>();
        pageResult.setList(alarms);
        pageResult.setTotal(total);
        return pageResult;
    }


    @Override
    public PageResult<increaseAlarmVo> getIncreaseAlarm(NewCheckAlarmReqVO reqVO) {
        PageResult<increaseAlarmVo> pageResult = new PageResult<>();

        try {
            if (reqVO.getDeptIds() == null || reqVO.getDeptIds().isEmpty()) {
                reqVO.setDeptIds(getDeptIds(reqVO.getDeptId()));
            }

            // 创建部门临时表
            followUpMapper.createTmpDeptTable();
            // 批量插入 deptIds
            if (reqVO.getDeptIds() != null && !reqVO.getDeptIds().isEmpty()) {
                followUpMapper.batchInsertDeptIds(reqVO.getDeptIds());
            }

            //查询最新随访表的3级及以上的随访人员信息
            List<increaseAlarmVo>  result = followUpMapper.getIncreaseAlarm(reqVO);

            Long  total = followUpMapper.getIncreaseAlarmTotal(reqVO);
            // 根据 id_number 批量查 五包一 里面有哪些存在,查询五包一数据判断是否有数据，如果查到就把这个字段boolFiveInOne赋值为1，没有查到就赋值为0
            // 先取出来所有 id_number
            List<String> idNumbers = result.stream()
                    .map(increaseAlarmVo::getIdNumber)
                    .filter(Objects::nonNull)
                    .distinct()
                    .collect(Collectors.toList());

            if (!idNumbers.isEmpty()) {
                // 批量查存在的 id_number
                List<String> existsIds = followUpMapper.selectExistFiveInOneIdNumbers(idNumbers);
                // 变成 HashSet 提升 contains 效率
                Set<String> existsIdSet = new HashSet<>(existsIds);

                // 给每个 VO 设置 boolFiveInOne
                for (increaseAlarmVo vo : result) {
                    if (existsIdSet.contains(vo.getIdNumber())) {
                        vo.setBoolFiveInOne("1");
                    } else {
                        vo.setBoolFiveInOne("0");
                    }
                }
            }

            pageResult.setList(result);
            pageResult.setTotal(total);
        } finally {
            followUpMapper.dropTmpDeptTable();
        }

        return pageResult;
    }

    @Override
    public PageResult<NewCheckAlarmRespVO> getNewCheckAlarm(NewCheckAlarmReqVO reqVO) {
        if (reqVO.getDeptIds() == null || reqVO.getDeptIds().isEmpty()) {
            reqVO.setDeptIds(getDeptIds(reqVO.getDeptId()));
        }

        // 1. 构造 MyBatis-Plus 的分页对象 (mapPage)
        Page<Map<String, Object>> mapPage =
                new Page<>(reqVO.getPageNo(), reqVO.getPageSize());
        // 获取一个月前的时间
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime oneMonthAgo = now.minusMonths(1);
        QueryWrapper<PatientInfoDO> wrapper = new QueryWrapper<>();
        wrapper.select(
                "id",
                "id_number",
                "patient_name",
                "address",
                "gender",
                "contact_person",
                "contact_phone",
                "report_time",
                "orgin_dept_id")
                .lt("report_time", oneMonthAgo)
                .isNull("diagnosis_status")
                .in("orgin_dept_id", reqVO.getDeptIds());

        // 可选的额外过滤
        if (StringUtils.isNotBlank(reqVO.getPatientName())) {
            wrapper.like("patient_name", reqVO.getPatientName());
        }
        if (StringUtils.isNotBlank(reqVO.getIdNumber())) {
            wrapper.eq("id_number", reqVO.getIdNumber());
        }

        IPage<Map<String, Object>> aggPage =
                reportMapper.selectMapsPage(mapPage, wrapper);
        // 2. 把 Map 结果转换为 VO，并按分页规则打上名次
        long startRank = (long)(reqVO.getPageNo() - 1) * reqVO.getPageSize() + 1;
        List<NewCheckAlarmRespVO> voList = new ArrayList<>(aggPage.getRecords().size());
        for (int i = 0; i < aggPage.getRecords().size(); i++) {
            Map<String, Object> row = aggPage.getRecords().get(i);
            NewCheckAlarmRespVO vo = new NewCheckAlarmRespVO();
            vo.setId((long) row.get("id"));
            vo.setIdNumber((String) row.get("id_number"));
            vo.setPatientName((String) row.get("patient_name"));
            vo.setAddress((String) row.get("address"));
            vo.setGender((String) row.get("gender"));
            vo.setContactPerson((String) row.get("contact_person"));
            vo.setContactPhone((String) row.get("contact_phone"));
            vo.setReportTime((LocalDateTime) row.get("report_time"));
            vo.setDeptId((Long) row.get("orgin_dept_id"));
            voList.add(vo);
        }
        PageResult<NewCheckAlarmRespVO> pageResult = new PageResult<>();
        pageResult.setTotal(aggPage.getTotal());
        pageResult.setList(voList);
        return pageResult;
    }

    @Override
    public PageResult<CureRateRespVO> getCureRate(PageParam pageParam) {
        // 1. 构造 MyBatis-Plus 的分页对象 (mapPage)
        Page<Map<String, Object>> mapPage =
                new Page<>(pageParam.getPageNo(), pageParam.getPageSize());

        // 2. 构造聚合查询：只统计 t.treatment_effect = 1 的"治愈"记录
        QueryWrapper<FollowUpDO> wrapper = new QueryWrapper<FollowUpDO>()
                .select("management_org_name", "COUNT(*) AS numberOfPeople")
                .eq("treatment_effect", 1)
                .groupBy("management_org_name")
                .orderByDesc("numberOfPeople");

        // 3. 执行分页聚合查询，返回 IPage<Map<String, Object>>
        IPage<Map<String, Object>> aggPage =
                followUpMapper.selectMapsPage(mapPage, wrapper);

        // 4. 把 Map 结果转换为 VO，并按分页规则打上名次
        long startRank = (long)(pageParam.getPageNo() - 1) * pageParam.getPageSize() + 1;
        List<CureRateRespVO> voList = new ArrayList<>(aggPage.getRecords().size());

        for (int i = 0; i < aggPage.getRecords().size(); i++) {
            Map<String, Object> row = aggPage.getRecords().get(i);
            CureRateRespVO vo = new CureRateRespVO();
            vo.setManagementOrgName((String) row.get("management_org_name"));
            vo.setNumberOfPeople(row.get("numberOfPeople").toString());
            vo.setRanking(String.valueOf(startRank + i));
            voList.add(vo);
        }

        // 5. 封装到 PageResult 并返回
        PageResult<CureRateRespVO> pageResult = new PageResult<>();
        pageResult.setTotal(aggPage.getTotal());
        pageResult.setList(voList);

        return pageResult;
    }

    @Override
    public BigScreenCheckPatient newPatientsCount(BigScreenReqVO reqVO) {
        BigScreenCheckPatient bigScreenCheckPatient = new BigScreenCheckPatient();
        // 获取部门IDs
        reqVO.setDeptIds(getDeptIds(reqVO.getDeptId()));

        String startTime = reqVO.getStartTime();
        //设置当前时间
        DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String endTime = LocalDateTime.now().format(FORMATTER);

        //查询新增患者数量
        bigScreenCheckPatient.setNewPatientsCount(
                bigScreenMapper.countNewPatients(startTime, endTime, reqVO.getDeptIds()));

        //查询鉴定新增患者数量
        bigScreenCheckPatient.setIdentifiedNewPatientsCount(
                bigScreenMapper.countIdentifiedNewPatients(startTime, endTime, reqVO.getDeptIds()));

        //查询新确诊患者数量
        bigScreenCheckPatient.setNewIdentifiedPatientsCount(
                bigScreenMapper.countNewIdentifiedPatients(startTime, endTime, reqVO.getDeptIds()));
        return bigScreenCheckPatient;
    }

    @Override
    public Double patientFaceVisitRate(BigScreenReqVO reqVO) {
        // 1. 准备参数
        reqVO.setDeptIds(getDeptIds(reqVO.getDeptId()));
        String startStr = reqVO.getStartTime();
        String endStr   = reqVO.getEndTime();

        // 2. 查询总人次 & 面访人次
        int totalCount = followUpMapper.selectTotalLatestFollowUps(
                reqVO.getDeptIds(), startStr, endStr);
        if (totalCount == 0) {
            return 0.0;
        }
        int faceCount  = followUpMapper.selectFaceLatestFollowUps(
                reqVO.getDeptIds(), startStr, endStr);

        // 3. 计算面访率
        BigDecimal face  = BigDecimal.valueOf(faceCount);
        BigDecimal total = BigDecimal.valueOf(totalCount);
        // divide 时 scale=2，RoundingMode.HALF_UP 四舍五入
        BigDecimal rate  = face
                .divide(total, 2, RoundingMode.HALF_UP);
        return rate.doubleValue();
    }

    @Override
    public List<LostContactCountRespVO> lostContactCount(BigScreenReqVO pageParam) {
        pageParam.setDeptIds(getDeptIds(pageParam.getDeptId()));
        String startStr = pageParam.getStartTime(); // 不再拿它当起点，只给 SQL 用
        String endStr   = pageParam.getEndTime();   // 这个才是真正的“窗口”结束点

        // 拿到“总开始前的失访”和“总区间内的所有随访”
        List<LostContactVO> newList      = followUpMapper.selectLostBeforeStart(pageParam.getDeptIds(), startStr);
        List<LostContactVO> rangeAllList = followUpMapper.selectLostBetween(pageParam.getDeptIds(), startStr, endStr);

        List<LostContactCountRespVO> result = new ArrayList<>();

        DateTimeFormatter DB_DATE_FMT = DateTimeFormatter.ofPattern("yyyyMMdd");
        DateTimeFormatter MONTH_FMT   = DateTimeFormatter.ofPattern("yyyy-MM");
        DateTimeFormatter dayLabelFmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");

        // 1. 解析结束时间为 LocalDate / YearMonth 做基准
        LocalDate endDate   = LocalDate.parse(endStr, DB_DATE_FMT);
        YearMonth endYm     = YearMonth.from(endDate);

        switch (pageParam.getTimeType()) {
            case "01":
                // 前 7 天：从 endDate.minusDays(6) 到 endDate
                for (int i = 6; i >= 0; i--) {
                    LocalDate day = endDate.minusDays(i);
                    String ts = day.format(DB_DATE_FMT);
                    String te = day.plusDays(1).format(DB_DATE_FMT);

                    List<LostContactVO> lost = mergeLostContacts(newList, rangeAllList, ts, te);
                    LostContactCountRespVO vo = new LostContactCountRespVO();
                    vo.setTime(day.format(dayLabelFmt));
                    vo.setCount((long) lost.size());
                    result.add(vo);
                }
                break;

            case "02":
                // 本月及前 5 个月：从 endYm.minusMonths(5) 到 endYm
                for (int i = 5; i >= 0; i--) {
                    YearMonth ym = endYm.minusMonths(i);
                    LocalDate monthStart = ym.atDay(1);
                    LocalDate monthEnd   = monthStart.plusMonths(1);
                    String ts = monthStart.format(DB_DATE_FMT);
                    String te = monthEnd.format(DB_DATE_FMT);

                    List<LostContactVO> lost = mergeLostContacts(newList, rangeAllList, ts, te);
                    LostContactCountRespVO vo = new LostContactCountRespVO();
                    vo.setTime(ym.format(MONTH_FMT));
                    vo.setCount((long) lost.size());
                    result.add(vo);
                }
                break;

            case "03":
                // 本月及前 11 个月：从 endYm.minusMonths(11) 到 endYm
                for (int i = 11; i >= 0; i--) {
                    YearMonth ym = endYm.minusMonths(i);
                    LocalDate monthStart = ym.atDay(1);
                    LocalDate monthEnd   = monthStart.plusMonths(1);
                    String ts = monthStart.format(DB_DATE_FMT);
                    String te = monthEnd.format(DB_DATE_FMT);

                    List<LostContactVO> lost = mergeLostContacts(newList, rangeAllList, ts, te);
                    LostContactCountRespVO vo = new LostContactCountRespVO();
                    vo.setTime(ym.format(MONTH_FMT));
                    vo.setCount((long) lost.size());
                    result.add(vo);
                }
                break;

            default:
                // 无效 timeType
        }

        return result;
    }

    public List<LostContactVO> mergeLostContacts(
            List<LostContactVO> beforeList,
            List<LostContactVO> rangeAllList,
            String totalStart,
            String periodEnd
    ) {
        DateTimeFormatter DB_DATE_FMT =
                DateTimeFormatter.ofPattern("yyyyMMdd");
        // 1. 构造时间边界
        LocalDate startDate = LocalDate.parse(totalStart, DB_DATE_FMT);
        LocalDate endDate   = LocalDate.parse(periodEnd, DB_DATE_FMT);

        // 2. 从 rangeAllList 过滤出在 [startDate, endDate] 内的记录，
        //    并按 idNumber 取最新一条（followUpDate 最大）
        Map<String, LostContactVO> latestMap = rangeAllList.stream()
                .filter(vo -> {
                    LocalDate d = LocalDate.parse(vo.getFollowUpDate(), DB_DATE_FMT);
                    return !d.isBefore(startDate) && !d.isAfter(endDate);
                })
                .collect(Collectors.toMap(
                        LostContactVO::getIdNumber,
                        Function.identity(),
                        (a, b) ->
                                // 冲突时保留 followUpDate 晚的那条
                                a.getFollowUpDate().compareTo(b.getFollowUpDate()) >= 0 ? a : b
                ));

        // 3. 结果列表 & 已处理 ID 集合
        List<LostContactVO> result = new ArrayList<>();
        Set<String> processed = new HashSet<>();

        // 4. 遍历 beforeList：若在 latestMap 中被“再管理”(status="1") 则剔除；
        //    否则保留 beforeList 的那条
        for (LostContactVO vo : beforeList) {
            LostContactVO latest = latestMap.get(vo.getIdNumber());
            if (latest != null && "1".equals(latest.getCurrentOrgSubDbStatus())) {
                processed.add(vo.getIdNumber());
                continue;
            }
            result.add(vo);
            processed.add(vo.getIdNumber());
        }

        // 5. 再把 latestMap 中 status="2"（仍是失访）的，且不在 processed 里的，加入结果
        for (LostContactVO latest : latestMap.values()) {
            if ("2".equals(latest.getCurrentOrgSubDbStatus())
                    && !processed.contains(latest.getIdNumber())) {
                result.add(latest);
            }
        }

        return result;
    }

    @Override
    public SuspectedMentalDisorderVo suspectedMentalDisorder(BigScreenReqVO pageParam) {
        //返回数据
        SuspectedMentalDisorderVo vo = new SuspectedMentalDisorderVo();
        ArrayList<SpectedMentalDisorderVoDetail> spectedMentalDisorderVoDetails1 = new ArrayList<>();

        // 获取父级部门IDs 和部门名称信息
        List<DeptDO> deptIdsByParentId = getDeptIdsByParentId(pageParam.getDeptId());
        ArrayList<Long> longs = new ArrayList<>();
        for (DeptDO deptDO : deptIdsByParentId) {
            longs.add(deptDO.getId());
        }

        String startTime = pageParam.getStartTime();
        //设置当前时间
        DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String endTime = LocalDateTime.now().format(FORMATTER);


        for (DeptDO deptDO : deptIdsByParentId) {
            SpectedMentalDisorderVoDetail spectedMentalDisorderVoDetail = new SpectedMentalDisorderVoDetail();
            spectedMentalDisorderVoDetail.setDeptName(deptDO.getName());
            spectedMentalDisorderVoDetail.setDeptId(deptDO.getId());

            List<Long> deptIdsList = new ArrayList<>(deptService.getChildDeptIdListFromCache(deptDO.getId()));
            deptIdsList.add(deptDO.getId());
            spectedMentalDisorderVoDetail.setSuspectedMentalDisorderCount(bigScreenMapper.
                    suspectedMentalDisorder(startTime, endTime, deptIdsList));
            spectedMentalDisorderVoDetails1.add(spectedMentalDisorderVoDetail);
        }

        vo.setDetail(spectedMentalDisorderVoDetails1);
        return vo;
    }


    @Override
    public String alarmMessage(List<AlarmMessageReqVO> reqVOS) {
        // 1. 提取所有 deptId
        Set<Long> deptIds = reqVOS.stream()
                .map(AlarmMessageReqVO::getDeptId)
                .collect(Collectors.toSet());

        // 2. 一次性批量拉取所有 DeptDO
        List<DeptDO> deptList = deptService.getDeptList(new ArrayList<>(deptIds));
        Map<Long, DeptDO> deptMap = deptList.stream()
                .collect(Collectors.toMap(DeptDO::getId, Function.identity()));

        List<String> missing = new ArrayList<>();

        // 3. 构造短信请求
        for (AlarmMessageReqVO vo : reqVOS) {
            DeptDO dept = deptMap.get(vo.getDeptId());
            if (dept == null || dept.getPhone() == null || dept.getPhone().isEmpty()) {
                // 手机号不存在或空
                missing.add(vo.getPatientName());
                continue;
            }

            SmsSendSingleToUserReqDTO dto = new SmsSendSingleToUserReqDTO();
            dto.setMobile(dept.getPhone());
            dto.setTemplateCode("alarm");
            dto.setTemplateParams(Collections.singletonMap("patientName", vo.getPatientName()));
            smsSendApi.sendSingleSmsToMember(dto);
        }

        // 5. 返回结果
        if (missing.isEmpty()) {
            return "发送短信成功";
        } else {
            String names = String.join("、", missing);
            return names + " 的部门没有设置手机号，请联系管理员设置";
        }
    }
}
